#pragma once

#include <cstdarg>

template<typename T>
class string_base
{
    public:
        string_base()
            : m_ptr(nullptr), m_len(0)
        {
        }
        string_base(size_t len) : m_len(len)
        {
            if(len < 1)
                m_ptr = nullptr;
            else
                m_ptr = new T[len];
        }
        ~string_base()
        {
            clear();
        }
        void clear()
        {
            m_len = 0;
            if(!m_ptr) return;
            delete[] m_ptr;
            m_ptr = nullptr;
        }
        string_base(const string_base &o)
        {
            m_ptr = new T[o.m_len];
            memcpy(m_ptr, o.m_ptr, m_len = o.m_len);
        }
        string_base(string_base &&o)
            : m_ptr(o.m_ptr), m_len(o.m_len)
        {
            o.m_ptr = nullptr;
            o.m_len = 0;
        }
        string_base &operator=(const string_base &o)
        {
            clear();
            m_ptr = new T[o.m_len];
            memcpy(m_ptr, o.m_ptr, m_len = o.m_len);
            return *this;
        }
        string_base &operator +=(const string_base &o)
        {
            if(o.m_len < 2)
                return *this;
            AutoDeletes<T> p(new T[size() + o.m_len]);
            if(size() > 0)
                memcpy(p, m_ptr, size());
            memcpy(p.get() + size(), o.m_ptr, o.m_len);
            m_ptr = p.move();
            m_len += o.size();
            return *this;
        }
        string_base operator+(const string_base &o)
        {
            string_base t(*this);
            t += o;
            return reinterpret_cast<string_base&&>(t);
        }
        operator bool() const { return !!m_ptr && m_len; }
        operator T*() { return m_ptr; }
        operator const T*() const { return m_ptr; }
        // T& operator [](size_t i) { return m_ptr[i]; }
        // const T& operator [](size_t i) const { return m_ptr[i]; }
        size_t size() const { return m_len ? m_len - 1 : 0; }
    protected:
        T *m_ptr;
        size_t m_len;
};
class astring : public string_base<char>
{
    public:
        astring() : string_base<char>() {}
        astring(const char *s);
        astring &operator=(const char *s);
        astring(size_t len) : string_base<char>(len) {}
        astring &fromUTF16(const wchar_t *p);
        astring &FormatV(const char *fmt, va_list ap);
        astring &Format(const char *fmt, ...);
        astring &AppendFormatV(const char *fmt, va_list ap);
        astring &AppendFormat(const char *fmt, ...);
};
class wstring : public string_base<wchar_t>
{
    public:
        wstring() : string_base<wchar_t>() {}
        wstring(const wchar_t *s);
        wstring &operator=(const wchar_t *s);
        wstring(size_t len) : string_base<wchar_t>(len) {}
        wstring &fromAnsi(const char *p);
        wstring &FormatV(const wchar_t *fmt, va_list ap);
        wstring &Format(const wchar_t *fmt, ...);
        wstring &AppendFormatV(const wchar_t *fmt, va_list ap);
        wstring &AppendFormat(const wchar_t *fmt, ...);
        wstring &replace(const wchar_t *from, const wchar_t *to);
        void swap(wstring &o);
};
class u8string : public string_base<char>
{
    public:
        u8string() : string_base<char>() {}
        u8string(size_t len) : string_base<char>(len) {}
        u8string &fromUTF16(const wchar_t *p);
        u8string &fromAnsi(const char *p);
        u8string &FormatV(const char *fmt, va_list ap);
        u8string &FormatV(const wchar_t *fmt, va_list ap);
        u8string &Append(const char *str);
        u8string &Append(const wchar_t *str);
        template<typename T> u8string &Format(const T *fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);

            FormatV(fmt, ap);
            va_end(ap);
            return *this;
        }
        template<typename T> u8string &AppendFormatV(const T *fmt, va_list ap)
        {
            u8string s;
            *this += s.FormatV(fmt, ap);
            return *this;
        }
        template<typename T> u8string &AppendFormat(const T *fmt, ...)
        {
            va_list ap;
            va_start(ap, fmt);
            AppendFormatV(fmt, ap);
            va_end(ap);
            return *this;
        }
};
